using System;
using System.Text;
using System.IO;

namespace Cassinipp.WebServer.Processors
{
    public class DirectoryListingProcessor : AbstractProcessor
	{
		private static readonly string[] DefaultFileNames = new [] {"default.aspx", "default.htm", "default.html"};

        private const string _dirListingFormat1 =
@"<html>
    <head>
    <title>Directory Listing -- {0}</title>
";

        private const string _dirListingFormat2 =
            @"    </head>
    <body bgcolor=""white"">

    <h2> <i>Directory Listing -- {0}</i> </h2></span>

            <hr width=100% size=1 color=silver>

<PRE>
";

        private static string _dirListingTail =
            @"</PRE>
            <hr width=100% size=1 color=silver>

            <b>Version Information:</b>&nbsp;Cassini Web Server " +
            Version.FullVersion + @"

            </font>

    </body>
</html>
";

        private const string _dirListingParentFormat =
            @"<A href=""{0}"">[To Parent Directory]</A>

";

        private const string _dirListingFileFormat =
            @"{0,38:dddd, MMMM dd, yyyy hh:mm tt} {1,12:n0} <A href=""{2}"">{3}</A>
";

        private const string _dirListingDirFormat =
            @"{0,38:dddd, MMMM dd, yyyy hh:mm tt}        &lt;dir&gt; <A href=""{1}/"">{2}</A>
";

        public override bool Intercept(Request request)
		{
			if (request.Verb != "GET")
			{
				return false;
			}

			var dirPathTranslated = request.PathTranslated;

			if (request.PathInfo.Length > 0)
			{
				// directory path can never have pathInfo
				dirPathTranslated = request.MapPath(request.Path);
			}

			if (!Directory.Exists(dirPathTranslated))
			{
				return false;
			}

			// have to redirect /foo to /foo/ to allow relative links to work
			if (!request.Path.EndsWith("/", StringComparison.Ordinal))
			{
				var newPath = request.Path + "/";
				var location = "Location: " + request.UrlEncodeRedirect(newPath) + "\r\n";
				var body = "<html><head><title>Object moved</title></head><body>\r\n" +
				              "<h2>Object moved to <a href='" + newPath + "'>here</a>.</h2>\r\n" +
				              "</body></html>\r\n";

				request.Connection.WriteEntireResponseFromString(302, location, body, false);

				return true;
			}

			// check for the default file
			foreach (string filename in DefaultFileNames)
			{
				if (File.Exists(request.MapPath(request.Path + "\\" + filename)))
				{
					// pretend the request is for the default file path
					request.ChangeTarget(request.Path + filename);

					return false; // go through normal processing
				}
			}

			// get all files and subdirs
			FileSystemInfo[] infos = null;
			try
			{
				infos = (new DirectoryInfo(dirPathTranslated)).GetFileSystemInfos();
			}
			catch
			{
			}

			// determine if parent is appropriate
			string parentPath = null;

			if (request.Path.Length > 1)
			{
				var i = request.Path.LastIndexOf('/', request.Path.Length - 2);

				parentPath = (i > 0) ? request.Path.Substring(0, i) : "/";
				if (!request.Host.IsVirtualPathInApp(parentPath))
				{
					parentPath = null;
				}
			}

			request.Connection.WriteEntireResponseFromString(200, "Content-type: text/html; charset=utf-8\r\n",
			                                                 FormatDirectoryListing(request.Path, parentPath, infos),
			                                                 false);
			return true;
		}

        public static string FormatDirectoryListing(string dirPath, string parentPath, FileSystemInfo[] elements)
        {
            StringBuilder sb = new StringBuilder();

            sb.Append(string.Format(_dirListingFormat1, dirPath));
            sb.Append(Messages.EmbeddedStyleSheet);
            sb.Append(string.Format(_dirListingFormat2, dirPath));

            if (parentPath != null)
            {
                if (!parentPath.EndsWith("/"))
                {
                    parentPath += "/";
                }
                sb.Append(string.Format(_dirListingParentFormat, parentPath));
            }

            if (elements != null)
            {
                for (int i = 0; i < elements.Length; i++)
                {
                    if (elements[i] is FileInfo)
                    {
                        FileInfo fi = (FileInfo)elements[i];
                        sb.Append(string.Format(_dirListingFileFormat,
                                                fi.LastWriteTime, fi.Length, fi.Name, fi.Name));
                    }
                    else if (elements[i] is DirectoryInfo)
                    {
                        DirectoryInfo di = (DirectoryInfo)elements[i];
                        sb.Append(string.Format(_dirListingDirFormat,
                                                di.LastWriteTime, di.Name, di.Name));
                    }
                }
            }

            sb.Append(_dirListingTail);
            return sb.ToString();
        }
	}
}